{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 函数\n",
    "函数是组织好的，可重复使用的，用来实现单一，或相关联功能的代码段。\n",
    "函数能提高应用的模块性，和代码的重复利用率。你已经知道Python提供了许多内建函数，比如print()。但你也可以自己创建函数，这被叫做用户自定义函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 定义一个函数\n",
    "你可以定义一个由自己想要功能的函数，以下是简单的规则：\n",
    "* 函数代码块以 def 关键词开头，后接函数标识符名称和圆括号 ()。\n",
    "* 任何传入参数和自变量必须放在圆括号中间，圆括号之间可以用于定义参数。\n",
    "* 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。\n",
    "* 函数内容以冒号起始，并且缩进。\n",
    "* return [表达式] 结束函数，选择性地返回一个值给调用方。不带表达式的return相当于返回 None。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 语法 \n",
    "> ``` def 函数名（参数列表）: \n",
    "    函数体\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "def hello(word):\n",
    "    print('hello %s' % word)\n",
    "hello('world')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello quant\n"
     ]
    }
   ],
   "source": [
    "hello('quant')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 参数传递\n",
    "在 python 中，类型属于对象，变量是没有类型的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "a=123\n",
    "a='hello'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 可更改(mutable)与不可更改(immutable)对象\n",
    "在 python 中，strings, tuples, 和 numbers 是不可更改的对象，而 list,dict 等则是可以修改的对象。\n",
    "* 不可变类型：变量赋值 a=5 后再赋值 a=10，这里实际是新生成一个 int 值对象 10，再让 a 指向它，而 5 被丢弃，不是改变a的值，相当于新生成了a。\n",
    "* 可变类型：变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改，本身la没有动，只是其内部的一部分值被修改了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# python 函数的参数传递：\n",
    "* 不可变类型：类似 c++ 的值传递，如 整数、字符串、元组。如fun（a），传递的只是a的值，没有影响a对象本身。比如在 fun（a）内部修改 a 的值，只是修改另一个复制的对象，不会影响 a 本身。\n",
    "* 可变类型：类似 c++ 的引用传递，如 列表，字典。如 fun（la），则是将 la 真正的传过去，修改后fun外部的la也会受影响\n",
    "\n",
    "**python 中一切都是对象，严格意义我们不能说值传递还是引用传递，我们应该说传不可变对象和传可变对象。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# python 传不可变对象实例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11 a\n",
      "10 aa\n"
     ]
    }
   ],
   "source": [
    "def change(a):\n",
    "    a+=1\n",
    "    print(a,'a')\n",
    "aa=10\n",
    "change(aa)\n",
    "print(aa,'aa')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 传可变对象实例\n",
    "可变对象在函数里修改了参数，那么在调用这个函数的函数里，原始的参数也被改变了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 'hello'] a\n",
      "[1, 2, 3, 'hello'] aa\n"
     ]
    }
   ],
   "source": [
    "def tt(a):\n",
    "    a.append('hello')\n",
    "    print(a,'a')\n",
    "aa=[1,2,3]\n",
    "tt(aa)\n",
    "print(aa,'aa')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 参数\n",
    "以下是调用函数时可使用的正式参数类型：\n",
    "* 必需参数\n",
    "* 关键字参数\n",
    "* 默认参数\n",
    "* 不定长参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 必需参数\n",
    "必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "def func1(a,b,c):\n",
    "    return a+b+c\n",
    "print(func1(1,3,2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 关键字参数\n",
    "关键字参数和函数调用关系紧密，函数调用使用关键字参数来确定传入的参数值。\n",
    "使用关键字参数允许函数调用时参数的顺序与声明时不一致，因为 Python 解释器能够用参数名匹配参数值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：男，他的年龄是：24\n"
     ]
    }
   ],
   "source": [
    "def show(name,age,sex):\n",
    "    return \"这个人的名字叫：%s，他的性别是：%s，他的年龄是：%s\" % (name,sex,age)\n",
    "print(show(name='小明',age=24,sex='男'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：男，他的年龄是：23\n"
     ]
    }
   ],
   "source": [
    "print(show(age=23,name='小明',sex='男'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：男，他的年龄是：24\n"
     ]
    }
   ],
   "source": [
    "print(show('小明',24,'男'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：24，他的年龄是：男\n"
     ]
    }
   ],
   "source": [
    "print(show('小明','男',24))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 默认参数\n",
    "调用函数时，如果没有传递参数，则会使用默认参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：男，他的年龄是：12\n"
     ]
    }
   ],
   "source": [
    "def show(name,age,sex='男'):\n",
    "    return \"这个人的名字叫：%s，他的性别是：%s，他的年龄是：%s\" % (name,sex,age)\n",
    "print(show('小明',12))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个人的名字叫：小明，他的性别是：女，他的年龄是：12\n"
     ]
    }
   ],
   "source": [
    "print(show('小明',12,'女'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 不定长参数\n",
    "你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数，和上述2种参数不同，声明时不会命名。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "def qiuhe(a,*args):\n",
    "    print(type(args))\n",
    "    for x in args:\n",
    "        a=a+x\n",
    "    return a\n",
    "print(qiuhe(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "print(qiuhe(1,2,3,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "加了星号（*）的变量名会存放所有未命名的变量参数。如果在函数调用时没有指定参数，它就是一个空元组。我们也可以不向函数传递未命名的变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "21\n"
     ]
    }
   ],
   "source": [
    "def qiuhe(a,*b,c,d):\n",
    "    a=a+c+d\n",
    "    for x in b:\n",
    "        a+=x\n",
    "    return a\n",
    "print(qiuhe(1,2,3,4,c=5,d=6))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 匿名函数\n",
    "* python 使用 lambda 来创建匿名函数。\n",
    "* 所谓匿名，意即不再使用 def 语句这样标准的形式定义一个函数。\n",
    "    * lambda 只是一个表达式，函数体比 def 简单很多。\n",
    "    * lambda的主体是一个表达式，而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。\n",
    "    * lambda 函数拥有自己的命名空间，且不能访问自己参数列表之外或全局命名空间里的参数。\n",
    "    * 虽然lambda函数看起来只能写一行，却不等同于C或C++的内联函数，后者的目的是调用小函数时不占用栈内存从而增加运行效率。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "qiuhe=lambda a,b:a+b\n",
    "print(qiuhe(1,2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# return语句\n",
    "return [表达式] 语句用于退出函数，选择性地向调用方返回一个表达式。不带参数值的return语句返回None。之前的例子都没有示范如何返回数值，以下实例演示了 return 语句的用法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "def read(word):\n",
    "    return 'this word len is %d' % len(word)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "this word len is 11\n"
     ]
    }
   ],
   "source": [
    "print(read(\"hello world\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 变量作用域\n",
    "Python 中，程序的变量并不是在哪个位置都可以访问的，访问权限决定于这个变量是在哪里赋值的。\n",
    "变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种，分别是：\n",
    "* L （Local） 局部作用域\n",
    "* E （Enclosing） 闭包函数外的函数中\n",
    "* G （Global） 全局作用域\n",
    "* B （Built-in） 内建作用域\n",
    "\n",
    "以 L –> E –> G –>B 的规则查找，即：在局部找不到，便会去局部外的局部找（例如闭包），再找不到就会去全局找，再者去内建中找。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = int(2.9)  # 内建作用域\n",
    "g_count = 0  # 全局作用域\n",
    "def outer():\n",
    "    o_count = 1  # 闭包函数外的函数中\n",
    "    def inner():\n",
    "        i_count = 2  # 局部作用域"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "a=0 # 全局作用域\n",
    "def outer():\n",
    "#     a=1 # 闭包函数外的函数中\n",
    "    def inner():\n",
    "#         a=2 # 局部作用域\n",
    "        print(a)\n",
    "    inner()\n",
    "outer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python 中只有模块（module），类（class）以及函数（def、lambda）才会引入新的作用域，其它的代码块（如 if/elif/else/、try/except、for/while等）是不会引入新的作用域的，也就是说这些语句内定义的变量，外部也可以访问，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "if True:\n",
    "    a=1\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'a' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-68-873499f43b90>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mdel\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mtt\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[0ma\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[0mtt\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'a' is not defined"
     ]
    }
   ],
   "source": [
    "del a\n",
    "def tt():\n",
    "    a=1\n",
    "    print(a)\n",
    "tt()\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 全局变量和局部变量\n",
    "定义在函数内部的变量拥有一个局部作用域，定义在函数外的拥有全局作用域。\n",
    "\n",
    "局部变量只能在其被声明的函数内部访问，而全局变量可以在整个程序范围内访问。调用函数时，所有在函数内声明的变量名称都将被加入到作用域中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数内是局部变量 3\n",
      "函数外是全局变量 0\n"
     ]
    }
   ],
   "source": [
    "xa=0# 这是一个全局变量\n",
    "def qiuhe(a,b):\n",
    "    xa=a+b # 这里xa是一个局部变量\n",
    "    print('函数内是局部变量',xa)\n",
    "qiuhe(1,2)\n",
    "print(\"函数外是全局变量\",xa)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# global 和 nonlocal关键字\n",
    "* 当内部作用域想修改外部作用域的变量时，就要用到global和nonlocal关键字了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "123\n"
     ]
    }
   ],
   "source": [
    "num=1\n",
    "def func1():\n",
    "    global num # 需要使用global 关键字声明\n",
    "    print(num)\n",
    "    num=123\n",
    "    print(num)\n",
    "func1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 如果要修改嵌套作用域（enclosing 作用域，外层非全局作用域）中的变量则需要 nonlocal 关键字了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "111\n"
     ]
    }
   ],
   "source": [
    "def outer():\n",
    "    num=10\n",
    "    def inner():\n",
    "        nonlocal num # nonlocal 使用外层非全局作用域中的变量\n",
    "        print(num)\n",
    "        num=111\n",
    "        print(num)\n",
    "    inner()\n",
    "outer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 另外有一种特殊情况"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "112\n"
     ]
    }
   ],
   "source": [
    "a=111\n",
    "def tt():\n",
    "    b=a+1\n",
    "    print(b)\n",
    "tt()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
